<?php
/*doc
 * Tree 树型类(无限分类)
 *
用法:
import("ext.tree");
$tree = new tree($rs);
#返回数组
$rs = $tree->get(0);
#返回分类下的子分类ID数组
$tree->getSubId($id);
#返回父分类ID数组
$tree->getFatherId($id);

$this->result 分类完整数组
$this->arr    递归时,返回结果

#返回SELECT 列表
#数据源
$rs = $db->where("menu_id='{$menu_rs['id']}' AND fid!=$id AND id!=$id")->order('fid,porder,id')->findall();

#TEST
import('ext.tree');

$html = $tree->select(array(
		'name' => 'fid',	
		'value' => $fid,	
		));

表结构
CREATE TABLE `ic_category` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `fid` int(10) NOT NULL DEFAULT '0' COMMENT '上一级ID',
  `porder` int(5) NOT NULL DEFAULT '0' COMMENT '排序',
  `name2` varchar(50) NOT NULL COMMENT '自定义网址-别名',
  `menu_id` int(10) NOT NULL COMMENT '属于哪个模块',
  `keyword` varchar(300) NOT NULL COMMENT '分类关键字',
  `desc` varchar(500) NOT NULL COMMENT '分类描述',
  `show` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否显示.隐藏的分类下的信息可被调用',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=115 DEFAULT CHARSET=utf8 COMMENT='无限分类表';


doc*/
class EXT_Tree 
{
    private $result;
    private $tmp = array();
    private $arr;
    private $already = array();

	#递归保存的参数
    private $tree = array();
    private $html_select = '';
    /**
     * 构造函数
     *
     * @param array $result 树型数据表结果集
     * @param array $fields 树型数据表字段，array(分类id,父id)
     * @param integer $root 顶级分类的父id
     * @param integer $father 以父分类为索引的数组
     */
    public function __construct($result, $fields = array('id', 'fid'))
	{
        $this->result = $result;
        $this->fields = $fields;    
        $this->handler();
    }
	function handler()
	{		
		$tmp = $f = array();
		foreach($this->result as $k => $v)
		{
			$f[$v['fid']][$v['id']] = $v;
			$tmp[$v['id']] = $v;
		}
		$this->father = $f;
		$this->result = $tmp;
		$this->x($f,0);
		$this->tmp = @$this->tree['child'];
	}
	function getRs($id)
	{
		if(isset($this->result[$id]))
		{
			return $this->result[$id];
		}
		else
		{
			return false;
		}
	}


    /**
     * 树型数据表结果集处理
	 2014年2月7日 19:00:16  网络上找的,算法有误,不能随意移动分类,当
	 时,
	 会出错
     */
	function array_insert($str,$value)
	{
		$str = substr($str,1);
		$key = '';
		foreach(explode('_',$str) as $k => $v)
		{
			$key .= "['child']['{$v}']";
		}
		$code = '$this->tree'."{$key} = ".var_export($value,1).";";
		eval($code);
	}
	#递归
	function x($f,$fid,$key = '')
	{		
		if(isset($f[$fid]))
		{
			foreach($f[$fid] as $k0 => $v0)
			{
				$k0 = "{$key}_{$k0}";
				//$this->tree[$k0] = $v0;
				$this->array_insert($k0,$v0);
				if(isset($v0['id']))
				{
					$this->x($f,$v0['id'],$k0);
				}
			}
		}
		
	}
	
   
    /**
     * 菜单 多维数组
     *
     * @param integer $id 分类id
     * @return array 返回分支，默认返回整个树
     */
    public function get($id = null)
	{
        return $this->tmp;
    }
    /**
     * 导航 一维数组
     *
     * @param integer $id 分类id
     * @return array 返回单线分类直到顶级分类
     */
   
    /**
     * 散落 一维数组
     *
     * @param integer $id 分类id
     * @return array 返回leaf下所有分类id
     */
	private function _subid($id)
	{
		if(!isset($this->result[$id]['id']))
		{
			return false;
		}
		$this->arr[] = $this->result[$id]['id'];
		$fid = $this->result[$id]['id'];
		if(isset($this->father[$fid]))
		{
			foreach($this->father[$fid] as $k => $v)
			{
				$this->_subid($v['id']);
			}
		}
	}
    public function subid($id)
	{
		$this->arr = array();
		$this->_subid($id);
		return $this->arr;
    }
	#别名
	public function getSubId($id)
	{
		$this->arr = array();
		$this->_subid($id);
		return $this->arr;
    }
	#得到父ID,返回ARRAY
	function getFatherId($id)
	{
		$this->arr = array();
		while(isset($this->result[$id]) && $this->result[$id]['fid'])
		{			
			$this->arr[] = $this->result[$id]['fid'];
			$id = $this->result[$id]['fid'];
		}
		return $this->arr;
	}


	//返回SELECT
	function select($arr = array(
	'name' => 'select',
	'value' => '',
	'tree_id' => 0,	//分类ID
	'default' => 0,	//默认值 -1 表示从根分类开始
	'class' => '',	//默认值
	))
	{
		extract($arr);
		isset($tree_id) || $tree_id = 0;
		isset($value) || $value = 0;
		isset($default) || $default = 0;
		isset($class) || $class = 'form-control';

		$rs = $this->get($tree_id);
		$this->html_select($rs,"/",$value);

		$html = "<select name='{$name}' id='{$name}' class='{$class}'>";
		if($default >= 0)
		{
			$html .= "<option value='{$default}'>/</option>";	
		}
		
		$html .= $this->html_select; 
		$html .= "</select>"; 
		$this->html_select = '';
		return $html;
	}
	function html_select($rs,$sep = '',$value = '')
	{
		$cnt = 1;
		$bak_sep = $sep;
		//$sep .= "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

		if(!$rs)
		{
			$rs = array();
		}
		foreach($rs as $k => $v)
		{
			if($v['fid'] == 0)
			{
				$sep = '/';
			}
			if(!isset($v['child']))
			{
				$sep = $bak_sep;
			}
			$this->html_select .= "<option value='{$v['id']}'";	
			if($value == $v['id'])
			{
				$this->html_select .= " selected ";	
			}
			$this->html_select .= ">{$sep}{$v['name']}";	
			#一般用于显示文章数
			if(isset($v['total']))
			{
				$this->html_select .= "({$v['total']})";	
			}

			$this->html_select .= "</option>";	
			$cnt++;
			if(isset($v['child']))
			{
				$sep .= "{$v['name']}/";		
				$this->html_select($v['child'],$sep,$value);
			}			
		}
	}
}
?>